#include <stdio.h>
#include <stdlib.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>

#include "nio.h"

static inline int realKqueueD(void *nio){
	int kq = (int)nio;
	return kq - 1;
}

void* nioInit(){
	int kq = kqueue();
	if(kq == -1){
		return NULL;
	}
	return (void *)(size_t)(kq + 1);
}


int nioRegisteFd(void* nio, int fd, int actions){
	int kq = realKqueueD(nio);
	struct kevent monitorEvent;
	int filters = 0;
	int result = 0;
	if(actions & ACTION_READ){
		EV_SET(&monitorEvent, fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0);
		result = kevent(kq, &monitorEvent, 1, NULL, 0, NULL);
		if(result < 0)
			return result;
	}
	if(actions & ACTION_WRITE){
		EV_SET(&monitorEvent, fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, 0, 0, 0);
		result = kevent(kq, &monitorEvent, 1, NULL, 0, NULL);
		if(result < 0)
			return result;
	}
	return result;
}

int nioUnRegisteFd(void* nio, int fd, int actions){
	int kq = realKqueueD(nio);
	struct kevent monitorEvent;
	int result = 0;
	if(actions & ACTION_READ){
		EV_SET(&monitorEvent, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
		result = kevent(kq, &monitorEvent, 1, NULL, 0, NULL);
		if(result < 0)
			return result;
	}
	if(actions & ACTION_WRITE){
		EV_SET(&monitorEvent, fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
		result = kevent(kq, &monitorEvent, 1, NULL, 0, NULL);
		if(result < 0){
			return result;
		}
	}
	return 0;
}

int nioWait(void* nio, struct FiredEvent* firedEvent, int firedEventLen){
	struct kevent events[firedEventLen];
	int changeNo = kevent(realKqueueD(nio), NULL, 0, events, firedEventLen, NULL);
	if(changeNo > 0){
		int index;
		for(index = 0; index < changeNo; index++){
			struct kevent event = events[index];
			firedEvent[index].fd = event.ident;
			if(event.filter == EVFILT_READ){
				firedEvent[index].action = ACTION_READ;
			}else if(event.filter == EVFILT_WRITE){
				firedEvent[index].action = ACTION_WRITE;
			}else{
				firedEvent[index].action = 0;				
			}
		}
	}	
	return changeNo;
}

void nioClose(void* nio){
	close(realKqueueD(nio));
}
